﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Threading;
using System.Text;
using System.Net;

namespace WCYCReader.Utility.SocketHelper
{
    /// <summary>
    /// MSDN 文档中的 tcp 示例代码，在本 demo 工程中没有使用该类，仅作为参考
    /// 网址：http://msdn.microsoft.com/zh-cn/library/windowsphone/develop/hh202858(v=vs.105).aspx
    /// </summary>
    public class MsdnSocketClient
    {
        // 在这个类的对象的生命周期中会在每次调用中使用
        Socket _socket = null;

        // 同步对象，用来通知一个异步操作已经完成
        static ManualResetEvent _clientDone = new ManualResetEvent(false);

        // 为每次异步调用声明一个毫秒级的超时时间，如果在这个时间内没有收到回应，那么这个调用就会被终止
        const int Timeout_Milliseconds = 50000;

        // 控制异步套接字的方法所使用的缓冲区的最大尺寸
        const int Max_Buffer_Size = 1024 * 4;

        /// <summary>
        /// 尝试向一个给定的服务器和端口发起一个 Tcp 套接字的链接请求
        /// </summary>
        /// <param name="hostName">服务器的 name</param>
        /// <param name="portNumber">连接的端口</param>
        /// <returns>返回异步套接字的操作结果</returns>
        public string Connect(string hostName, int portNumber)
        {
            string result = string.Empty;

            // 将网络终结点表示为主机名或 IP 地址和端口号的字符串表示形式。
            DnsEndPoint hostEntry = new DnsEndPoint(hostName, portNumber);

            _socket = new Socket(AddressFamily.InterNetwork, //  IP 版本 4 的地址。
                                          SocketType.Stream, 
                                          ProtocolType.Tcp);

            // 创建一个 SocketAsyncEventArgs 对象，在连接请求中使用
            SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
            socketEventArg.RemoteEndPoint = hostEntry;

            // 把事件委托调用采用内联的方式，以使这个方法包含在内部
            socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
            {
                // 获取连接请求的结果
                result = e.SocketError.ToString();

                // 通知请求结束，解除对 UI 线程的阻塞
                _clientDone.Set();
            });

            // 将事件状态设置为非终止状态，从而导致线程受阻。
            _clientDone.Reset();

            // 开始一个对远程主机连接的异步请求。
            _socket.ConnectAsync(socketEventArg);

            // 阻止 UI 线程，最多 TIMEOUT_MILLISECONDS 时间，如果在指定时间没有响应则继续向下执行
            _clientDone.WaitOne(Timeout_Milliseconds);

            return result;
        }

        /// <summary>
        /// 使用建立的连接给服务器发送数据
        /// </summary>
        /// <param name="data">发送到服务器端的数据</param>
        /// <returns>返回发送请求的结果</returns>
        public string Send(string data)
        {
            string response = "Operation Timeout";

            // 重复使用在 Connect 方法中创建的 _socket 对象
            if (_socket != null)
            {
                // 创建一个 SocketAsyncEventArgs 上下文对象
                SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();

                // 获取异步操作的远程 IP 或 DNS 终结点。
                socketEventArg.RemoteEndPoint = _socket.RemoteEndPoint;
                socketEventArg.UserToken = null;

                socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
                {
                    response = e.SocketError.ToString();

                    // Unblock the UI thread
                    _clientDone.Set();
                });

                // 把将被发送的数据添加到一个二进制数组中
                byte[] payload = Encoding.UTF8.GetBytes(data);
                //  设置要用于异步套接字方法的数据缓冲区。
                socketEventArg.SetBuffer(payload, 0, payload.Length);
                              
                _clientDone.Reset();

                // 将数据异步发送到连接的 Socket 对象
                _socket.SendAsync(socketEventArg);

           
                _clientDone.WaitOne(Timeout_Milliseconds);
            }
            else
            {
                response = "Socket is not initialized";
            }

            return response;
        }


        /// <summary>
        /// 使用既定的套接字连接从服务器接收数据
        /// </summary>
        /// <returns>返回从服务器端接收的数据</returns>
        public string Receive()
        {
            string response = "Operation Timeout";
                      
            if (_socket != null)
            {
                SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
                socketEventArg.RemoteEndPoint = _socket.RemoteEndPoint;

                // 设置要用于异步套接字方法的数据缓冲区
                socketEventArg.SetBuffer(new Byte[Max_Buffer_Size], 0, Max_Buffer_Size);
                               
                socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
                {
                    if (e.SocketError == SocketError.Success)
                    {
                        // Retrieve the data from the buffer
                        response = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
                        response = response.Trim('\0');
                    }
                    else
                    {
                        response = e.SocketError.ToString();
                    }

                    _clientDone.Set();
                });

                _clientDone.Reset();

                _socket.ReceiveAsync(socketEventArg);

                _clientDone.WaitOne(Timeout_Milliseconds);
            }
            else
            {
                response = "Socket is not initialized";
            }

            return response;
        }

        /// <summary>
        /// 关闭套接字连接，并且释放所有相关资源
        /// </summary>
        public void Close()
        {
            if (_socket != null)
            {
                _socket.Close();
            }
        }
    }
}
